package com.fxz.oauth.security.filter;

import com.fasterxml.jackson.databind.JsonNode;
import com.fasterxml.jackson.databind.ObjectMapper;
import lombok.RequiredArgsConstructor;
import org.springframework.security.authentication.BadCredentialsException;
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.AuthenticationException;
import org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter;

import javax.servlet.ServletInputStream;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;

/**
 * 自定义的用户名密码认证过滤器
 *
 * @author Fxz
 * @version 1.0
 * @date 2021-08-03 15:00
 */
@RequiredArgsConstructor
public class ResAuthenticationFilter extends UsernamePasswordAuthenticationFilter {

    private final ObjectMapper objectMapper;

    /**
     * 认证的方法
     *
     * @param request
     * @param response
     * @return
     * @throws AuthenticationException
     */
    @Override
    public Authentication attemptAuthentication(HttpServletRequest request, HttpServletResponse response) throws AuthenticationException {
        UsernamePasswordAuthenticationToken authenticationToken;
        try {
            //在请求中读取出用户名、密码
            ServletInputStream inputStream = request.getInputStream();
            JsonNode jsonNode = objectMapper.readTree(inputStream);
            String username = jsonNode.get("username").textValue();
            String password = jsonNode.get("password").textValue();

            authenticationToken = new UsernamePasswordAuthenticationToken(username, password);
        } catch (IOException e) {
            e.printStackTrace();
            throw new BadCredentialsException("没有找到用户名或密码");
        }
        setDetails(request, authenticationToken);
        //通过AuthenticationManager进行认证
        //获取到AuthenticationManager里面的所有AuthenticationProvider
        //判断provider是否支持传进去的UsernamePasswordAuthenticationToken
        //发现DaoAuthenticationProvider支持此token
        //但是DaoAuthenticationProvider没有authenticate方法
        //进入DaoAuthenticationProvider的父类AbstractUserDetailsAuthenticationProvider的authenticate方法
        //检查缓存中是否有用户 没有的话调用retrieveUser方法 retrieveUser方法调用userDetailsService根据用户名获取到用户详细信息
        //进行一系列检查 将用户信息放到缓存
        //最后创建一个成功的Authentication(调用DaoAuthenticationProvider的createSuccessAuthentication)
        //会进行一个密码的更新(旧加密编码)
        //最后返回一个完整的UsernamePasswordAuthenticationToken
        return this.getAuthenticationManager().authenticate(authenticationToken);
    }
}
